#!/usr/bin/env python
from __future__ import division
import sys
sys.settrace

import random, argparse, Queue, time, gc, traceback

from datetime import timedelta, datetime
from multiprocessing import Process, Event, JoinableQueue

import sql, lib

from tracklist import TrackList
from sublist import SubList
from holidaylist import HolidayList
from xdh_logging import LogWriter, initLog
from settings import settings
from cache import cache
from MPD import MPD
from playfile import PlayFile
from played_updater import PlayedUpdater
from tpd import TPD
from podcast import Podcast
from lastfm import LastFM

log = initLog("playlist")

class Playlist(object):
	def __init__(self):
		self.logWriter = LogWriter()
		self.sqlWriter = sql.RowWriter()
		self.tracklist = TrackList()
		self.lastfm = LastFM()
		self.playedUpdater = PlayedUpdater(self.lastfm)
		self.sublist = SubList()
		self.holidaylist = HolidayList()
		self.podcast = Podcast()

	def __enter__(self):
		self.logWriter.__enter__()
		self.sqlWriter.__enter__()
		self.tracklist.__enter__()
		self.sublist.__enter__()
		self.holidaylist.__enter__()
		self.lastfm.__enter__()
		self.playedUpdater.__enter__()
		self.podcast.__enter__()
		return self

	def __exit__(self, type, value, traceback):
		self.podcast.__exit__(type, value, traceback)
		self.playedUpdater.__exit__(type, value, traceback)
		self.lastfm.__exit__(type, value, traceback)
		self.holidaylist.__exit__(type, value, traceback)
		self.sublist.__exit__(type, value, traceback)
		self.tracklist.__exit__(type, value, traceback)
		self.sqlWriter.__exit__(type, value, traceback)
		self.logWriter.__exit__(type, value, traceback)

	@classmethod
	def get_args(cls):
		parser = argparse.ArgumentParser()
		parser.add_argument("--updatedb", help="Updates the database with the current structure for the tracks in the filesystem.", action="store_true")
		parser.add_argument("--recalctpd", help="Only recalculate Tracks Per Day.", action="store_true")
		parser.add_argument("--recalcpods", help="Only recalculate Podcast Feed Average Length.", action="store_true")
		parser.add_argument("--updatepods", help="Forces updating of the podcast tables from the defined feeds.", action="store_true")
		cls.args = parser.parse_args()


	@classmethod
	def add_new_tracks_to_MPD(cls, original):
		"""
		Takes the set of tracks that have been produced from SubList
		to be the new playlist. Determines what tracks are new,
		and adds them to mpd.
		"""
        
		log.info("Determining tracks to add to the MPD playlist..")
		play = PlayFile()
		added = list(play.playlist - original)
        
		remain = original - set(added)
        
		log.info("Kept {count} out of {ttl} tracks from original.".format(count = len(remain), ttl = len(original)))
        
		random.shuffle(added)
        
		log.info("Adding {count} new track{plural} to the MPD playlist.".format(count = len(added), plural = "s" if len(added) != 1 else ""))
		MPD.extend(added)
        
		log.info("Reconfiguring MPD")
		MPD.configure()
        
		log.info("Setting MPD to play.")
		MPD.play = True
        
		cache['latest_date'] = datetime.today()
        
		log.info("Playlist processing completed. {total} in list, {added} added.".format(total = len(play.playlist), added = len(added)))

	@classmethod
	def get_playlist(cls):
		play = PlayFile()
		return set(play.playlist)

	@classmethod
	def startup(cls):
		with Playlist() as playlist:
			try:
				cls.get_args()
                        
				if cls.args.recalctpd:
					log.info("Recalculating Tracks Per Day.")
					TPD.calc()
                        
				if cls.args.recalcpods:
					log.info("Recalculating Podcast Feed Average Length.")
					playlist.podcast.calc()
                        
				elif cls.args.updatedb:
					log.debug("Updating Tracklist.")
					playlist.tracklist.start()
					while playlist.tracklist.running():
						time.sleep(settings.data.sleep)
					playlist.tracklist.stop()
					log.debug("Finished updating Tracklist.")

				elif cls.args.updatepods:
					log.debug("Updating Podcasts.")
					playlist.podcast.start()
					while playlist.podcast.running:
						time.sleep(settings.data.sleep)
					playlist.podcast.stop()
					log.debug("Finished updating Podcasts.")
                        
				else:
					original = cls.get_playlist()
                        
					playlist.playedUpdater.initialize()
					while playlist.playedUpdater.updating:
						time.sleep(settings.data.sleep)

					playlist_updated = False
					if MPD.current != None:
						playlist.lastfm.now_playing(lib.safe_unicode(MPD.current))
					while True:
						playlist.lastfm.check()

						playlist.podcast.check()

#						print playlist.sublist.running, playlist.holidaylist.running, playlist_updated, playlist.playedUpdater.processable
						if not playlist.sublist.running and not playlist.holidaylist.running and playlist_updated:
							playlist.holidaylist.stop()
							playlist.sublist.stop()
							cls.add_new_tracks_to_MPD(original)
							original = cls.get_playlist()
							playlist_updated = False

						playlist.playedUpdater.processable = not playlist.sublist.running and not playlist.holidaylist.running and not playlist_updated

						playlist.playedUpdater.check()

						if not playlist.playedUpdater.updating and not playlist.sublist.running and not playlist.holidaylist.running and not playlist_updated:
							if len(cls.get_playlist()) < TPD.get_min() and not playlist.sublist.running:
								TPD.calc()
								original = cls.get_playlist()
								PlayFile.clear_used_tracks()
								playlist.sublist.start()
								playlist.holidaylist.start()
								playlist_updated = True

						time.sleep(settings.data.sleep)
			except Exception, e:
				log.error("{name}: {msg}".format(name = e.__class__.__name__, msg = e))
				log.error("".join(traceback.format_exception(*sys.exc_info())))


if __name__ == "__main__":
	Playlist.startup()
